// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:pub/src/exit_codes.dart' as exit_codes;
import 'package:test/test.dart';
import '../../descriptor.dart' as d;
import '../../test_pub.dart';

void main() {
  test('adds a package from git', () async {
    ensureGit();

    await d.git('foo.git', [
      d.libDir('foo'),
      d.libPubspec('foo', '1.0.0'),
    ]).create();

    await d.appDir(dependencies: {}).create();

    await pubAdd(args: ['foo', '--git-url', '../foo.git']);

    await d.dir(cachePath, [
      d.dir('git', [
        d.dir('cache', [d.gitPackageRepoCacheDir('foo')]),
        d.gitPackageRevisionCacheDir('foo'),
      ]),
    ]).validate();

    await d
        .appDir(
          dependencies: {
            'foo': {'git': '../foo.git'},
          },
        )
        .validate();
  });

  test('adds a package from git with relative url and --directory', () async {
    ensureGit();

    await d.git('foo.git', [
      d.libDir('foo'),
      d.libPubspec('foo', '1.0.0'),
    ]).create();

    await d.appDir(dependencies: {}).create();

    await pubAdd(
      args: ['--directory', appPath, 'foo', '--git-url', 'foo.git'],
      workingDirectory: d.sandbox,
      output: contains('Changed 1 dependency in `myapp`!'),
    );

    await d.dir(cachePath, [
      d.dir('git', [
        d.dir('cache', [d.gitPackageRepoCacheDir('foo')]),
        d.gitPackageRevisionCacheDir('foo'),
      ]),
    ]).validate();

    await d
        .appDir(
          dependencies: {
            'foo': {'git': '../foo.git'},
          },
        )
        .validate();
  });

  test('fails with invalid --git-url', () async {
    ensureGit();

    await d.git('foo.git', [
      d.libDir('foo'),
      d.libPubspec('foo', '1.0.0'),
    ]).create();

    await d.appDir(dependencies: {}).create();

    await pubAdd(
      args: ['foo', '--git-url', ':'],
      error: contains(
        'The --git-url must be a valid url: Invalid empty scheme.',
      ),
      exitCode: exit_codes.USAGE,
    );
  });

  test('adds a package from git with version constraint', () async {
    ensureGit();

    await d.git('foo.git', [
      d.libDir('foo'),
      d.libPubspec('foo', '1.0.0'),
    ]).create();

    await d.appDir(dependencies: {}).create();

    await pubAdd(args: ['foo:1.0.0', '--git-url', '../foo.git']);

    await d.dir(cachePath, [
      d.dir('git', [
        d.dir('cache', [d.gitPackageRepoCacheDir('foo')]),
        d.gitPackageRevisionCacheDir('foo'),
      ]),
    ]).validate();

    await d
        .appDir(
          dependencies: {
            'foo': {'git': '../foo.git', 'version': '1.0.0'},
          },
        )
        .validate();
  });

  test('fails when adding with an invalid version constraint', () async {
    ensureGit();

    await d.git('foo.git', [
      d.libDir('foo'),
      d.libPubspec('foo', '1.0.0'),
    ]).create();

    await d.appDir(dependencies: {}).create();

    await pubAdd(
      args: ['foo:2.0.0', '--git-url', '../foo.git'],
      error: equalsIgnoringWhitespace(
        'Because myapp depends on foo 2.0.0 from git which doesn\'t match '
        'any versions, version solving failed.',
      ),
      exitCode: exit_codes.DATA,
    );

    await d.appDir(dependencies: {}).validate();
    await d.dir(appPath, [
      d.nothing('.dart_tool/package_config.json'),
      d.nothing('pubspec.lock'),
    ]).validate();
  });

  test('fails when adding from an non-existing url', () async {
    ensureGit();

    await d.appDir(dependencies: {}).create();

    await pubAdd(
      args: ['foo', '--git-url', '../foo.git'],
      error: contains(
        'Unable to resolve package "foo" with the given '
        'git parameters',
      ),
      exitCode: exit_codes.DATA,
    );

    await d.appDir(dependencies: {}).validate();
    await d.dir(appPath, [
      d.nothing('.dart_tool/package_config.json'),
      d.nothing('pubspec.lock'),
    ]).validate();
  });

  test('fails if git-url is not declared', () async {
    ensureGit();

    await d.appDir(dependencies: {}).create();

    await pubAdd(
      args: ['foo', '--git-ref', 'master'],
      error: contains('The `--git-url` is required for git dependencies.'),
      exitCode: exit_codes.USAGE,
    );

    await d.appDir(dependencies: {}).validate();
    await d.dir(appPath, [
      d.nothing('.dart_tool/package_config.json'),
      d.nothing('pubspec.lock'),
    ]).validate();
  });

  test('can be overriden by dependency override', () async {
    final server = await servePackages();
    server.serve('foo', '1.2.2');

    await d.git('foo.git', [
      d.libDir('foo'),
      d.libPubspec('foo', '1.0.0'),
    ]).create();

    await d.dir(appPath, [
      d.pubspec({
        'name': 'myapp',
        'dependencies': {},
        'dependency_overrides': {'foo': '1.2.2'},
      }),
    ]).create();

    await pubAdd(args: ['foo', '--git-url', '../foo.git']);

    await d.cacheDir({'foo': '1.2.2'}).validate();
    await d.appPackageConfigFile([
      d.packageConfigEntry(name: 'foo', version: '1.2.2'),
    ]).validate();
    await d.dir(appPath, [
      d.pubspec({
        'name': 'myapp',
        'dependencies': {
          'foo': {'git': '../foo.git'},
        },
        'dependency_overrides': {'foo': '1.2.2'},
      }),
    ]).validate();
  });

  test('fails if multiple packages passed for git source', () async {
    ensureGit();

    await d.appDir(dependencies: {}).create();

    await pubAdd(
      args: ['foo', 'bar', 'baz', '--git-url', '../foo.git'],
      exitCode: exit_codes.USAGE,
      error: contains('Specify multiple git packages with descriptors.'),
    );
  });

  test('Can add a package with a git descriptor and relative path', () async {
    await d.git('foo.git', [
      d.dir('subdir', [d.libPubspec('foo', '1.2.3')]),
    ]).create();
    await d.appDir(dependencies: {}).create();
    await pubAdd(
      args: [
        '--directory',
        appPath,
        'foo:{"git": {"url":"foo.git", "path":"subdir"}}',
      ],
      workingDirectory: d.sandbox,
      output: contains('Changed 1 dependency in `myapp`!'),
    );

    await d
        .appDir(
          dependencies: {
            'foo': {
              'git': {'url': '../foo.git', 'path': 'subdir'},
            },
          },
        )
        .validate();
  });

  test('Can add multiple git packages using descriptors', () async {
    ensureGit();

    await d.git('foo.git', [
      d.libDir('foo'),
      d.libPubspec('foo', '1.0.0'),
    ]).create();
    await d.git('bar.git', [
      d.libDir('foo'),
      d.libPubspec('bar', '1.0.0'),
    ]).create();

    await d.appDir(dependencies: {}).create();

    await pubAdd(
      args: ['foo:{"git":"../foo.git"}', 'bar:{"git":"../bar.git"}'],
    );

    await d.dir(appPath, [
      d.pubspec({
        'name': 'myapp',
        'dependencies': {
          'foo': {'git': '../foo.git'},
          'bar': {'git': '../bar.git'},
        },
      }),
    ]).validate();
  });

  test('Can add git tag_pattern using descriptors', () async {
    ensureGit();

    await d.git('foo.git', [
      d.libDir('foo'),
      d.libPubspec('foo', '1.0.0'),
    ]).create();

    await d.git('foo.git').tag('v1.0.0');

    await d.git('foo.git', [
      d.libDir('foo'),
      d.libPubspec('foo', '2.0.0'),
    ]).commit();
    await d.git('foo.git').tag('v2.0.0');
    await d.git('foo.git', [
      d.libDir('foo'),
      d.libPubspec('foo', '3.0.0'),
    ]).commit(); // Not tagged, we won't get this.
    await d
        .appDir(
          dependencies: {},
          pubspec: {
            'environment': {'sdk': '^3.9.0'},
          },
        )
        .create();

    await pubAdd(
      args: ['foo:{"git":{"url": "../foo.git", "tag_pattern":"v{{version}}"}}'],
      environment: {'_PUB_TEST_SDK_VERSION': '3.9.0'},
    );

    await d.dir(appPath, [
      d.pubspec({
        'name': 'myapp',
        'environment': {'sdk': '^3.9.0'},
        'dependencies': {
          'foo': {
            'git': {'url': '../foo.git', 'tag_pattern': 'v{{version}}'},
            'version': '^2.0.0',
          },
        },
      }),
    ]).validate();
  });
}
